﻿using Core.Entities;
using Core.Enums;
using Core.ObjectOperate;
using Core.ObjectOperate.ReflectionImpl;
using Core.Reader;
using Core.ReadWriter;
using Core.Tools;
using Core.Writer;
using System;
using System.Collections;
using System.IO;

namespace Core.Formats
{
    /// <summary>
    /// Eson 格式化器
    /// 支持 Table, Object, Array 三个格式
    /// 非常适合 Table 格式
    /// 优点：结构紧凑，解析，编码速度快，传输速度快。
    /// 缺点：结构不直观，不可美化。
    /// </summary>
    public class EsonFormat : IDocumentFormat
    {
        /// <summary>
        /// 将一个 .Net 对象写入到文本中
        /// </summary>
        /// <param name="Value">.Net 对象</param>
        /// <param name="Writer">文本写入器</param>
        public void Encode(object Value, TextWriter Writer)
        {
            string StringValue = GetString(Value);

            if (StringValue != null)
            {
                Writer.Write(StringValue);
            }
            else if (Value is IDataReader)
            {
                Encode((IDataReader)Value, Writer);
            }
            else if (Value is IList)
            {
                Encode((IList)Value, Writer);
            }
            else if (Value is IDictionary)
            {
                Encode((IDictionary)Value, Writer);
            }
            else
            {
                Encode(ObjectOperater.Create(Value.GetType()), Value, Writer);
            }
        }

        /// <summary>
        /// 反向转义 Eson 文本
        /// </summary>
        /// <param name="Text">Eson 文本</param>
        /// <returns>返回结果</returns>
        public unsafe string UnEscape(string Text)
        {
            fixed (char* LpText = Text)
            {
                return UnEscape(LpText, 0, Text.Length);
            }
        }

        /// <summary>
        /// 转义 Eson 文本
        /// </summary>
        /// <param name="Text">Eson 文本</param>
        /// <returns>返回结果</returns>
        public unsafe string Escape(string Text)
        {
            fixed (char* LpText = Text)
            {
                int Length = Text.Length;

                for (int i = 0; i < Text.Length; ++i)
                {
                    switch (LpText[i])
                    {
                        case '~':
                        case '|':
                        case '{':
                        case '}':
                            ++Length;
                            break;
                    }
                }

                if (Length == Text.Length)
                {
                    return Text;
                }

                string Result = new string('\0', Length);

                fixed (char* LpResult = Result)
                {
                    for (int i = 0, j = 0; i < Text.Length; ++i, ++j)
                    {
                        switch (LpText[i])
                        {
                            case '~':
                                LpResult[j] = '~';
                                ++j;
                                LpResult[j] = '1';
                                break;
                            case '|':
                                LpResult[j] = '~';
                                ++j;
                                LpResult[j] = '2';
                                break;
                            case '{':
                                LpResult[j] = '~';
                                ++j;
                                LpResult[j] = '3';
                                break;
                            case '}':
                                LpResult[j] = '~';
                                ++j;
                                LpResult[j] = '4';
                                break;
                            default:
                                LpResult[j] = LpText[i];
                                break;
                        }
                    }
                }

                return Result;
            }
        }

        /// <summary>
        /// 反向转义 Eson 文本
        /// </summary>
        /// <param name="Text">Eson 文本</param>
        /// <returns>返回结果</returns>
        private unsafe string UnEscape(char* LpText, int Begin, int End)
        {
            if (End < Begin)
            {
                throw new Exception("解析字符串时出错");
            }

            if (End == Begin)
            {
                return "";
            }

            int Count = 0;

            for (int i = Begin; i < End; ++i)
            {
                if (LpText[i] == '~')
                {
                    ++Count;
                }
            }

            if (Count == 0)
            {
                return new string(LpText, Begin, End - Begin);
            }

            Count = End - Begin - Count;

            string Result = new string('\0', Count);

            fixed (char* LpResult = Result)
            {
                for (int i = 0; i < Count; i++)
                {
                    if (LpText[i] == '~')
                    {
                        ++LpText;

                        switch (LpText[i])
                        {
                            case '1':
                                LpResult[i] = '~';
                                break;
                            case '2':
                                LpResult[i] = '|';
                                break;
                            case '3':
                                LpResult[i] = '{';
                                break;
                            case '4':
                                LpResult[i] = '}';
                                break;
                        }
                    }
                    else
                    {
                        LpResult[i] = LpText[i];
                    }
                }

                return Result;
            }
        }

        /// <summary>
        /// 将 Eson 文本对象转换为 .Net 对象
        /// </summary>
        /// <param name="Text">Eson 文本</param>
        /// <returns>返回 .Net 对象</returns>
        public object ParseValue(string Text)
        {
            switch (Text)
            {
                case "null":
                    return null;
                case "true":
                    return true;
                case "false":
                    return false;
            }

            if (Text.Length == 0)
            {
                return "";
            }

            switch (Text[0])
            {
                case 'S':
                    return Text.Substring(1);
                case 'D':
                    long Ticks;

                    if (long.TryParse(Text.Substring(1), out Ticks))
                    {
                        return new DateTime(Ticks);
                    }

                    return Text;
            }

            bool IsNegative;
            bool IsFloat;
            int Digit;
            int Float;

            if (NumberHelper.IsNumber(Text, 10, out IsNegative, out IsFloat, out Digit, out Float))
            {
                if (IsFloat)
                {
                    if (Digit + Float <= 15)
                    {
                        return double.Parse(Text);
                    }
                    else if (Digit + Float <= 28)
                    {
                        return decimal.Parse(Text);
                    }
                }
                else if (Digit <= 9)
                {
                    return int.Parse(Text);
                }
                else if (Digit <= 18)
                {
                    return long.Parse(Text);
                }
                else if (Digit <= 28)
                {
                    return decimal.Parse(Text);
                }

                return double.Parse(Text);
            }

            return Text;
        }

        /// <summary>
        /// 将数据读取器中的内容以 Eson 格式写入到文本写入器中
        /// </summary>
        /// <param name="Reader">数据读取器</param>
        /// <param name="Writer">文本写入器</param>
        public void Encode(IDataReader Reader, TextWriter Writer)
        {
            Writer.Write('{');

            switch (Reader.ObjectType)
            {
                case RWTypes.Table:
                    Writer.Write('T');
                    break;
                case RWTypes.Object:
                    Writer.Write('O');
                    break;
                case RWTypes.Array:
                    Writer.Write('A');
                    break;
                default:
                    throw new Exception("Unsupported type!");
            }

            Writer.Write('|');

            Writer.Write(Reader.Count);

            if (Reader.ObjectType != RWTypes.Array)
            {
                foreach (var Item in Reader.Fields)
                {
                    Writer.Write('|');

                    Writer.Write(Escape(Item));
                }
            }

            while (Reader.Read())
            {
                for (int i = 0; i < Reader.Count; i++)
                {
                    Writer.Write('|');

                    Encode(Reader[i], Writer);
                }
            }

            Writer.Write('}');
        }

        public void Encode(IList Reader, TextWriter Writer)
        {
            Writer.Write("{A|");

            Writer.Write(Reader.Count);
            
            for (int i = 0; i < Reader.Count; i++)
            {
                Writer.Write('|');

                Encode(Reader[i], Writer);
            }

            Writer.Write('}');
        }

        public void Encode(IDictionary Reader, TextWriter Writer)
        {
            Writer.Write("{O|");

            Writer.Write(Reader.Count);

            var Enumerator = Reader.GetEnumerator();

            while (Enumerator.MoveNext())
            {
                Writer.Write('|');

                Writer.Write(Escape(Enumerator.Key.ToString()));
            }

            Enumerator.Reset();

            while (Enumerator.MoveNext())
            {
                Writer.Write('|');
                
                Encode(Enumerator.Value, Writer);
            }

            Writer.Write('}');
        }
        
        public void Encode(IObjectOperater ObjectOperater, object Object, TextWriter Writer)
        {
            Writer.Write("{O|");

            Writer.Write(ObjectOperater.PropertiesCount);

            for (int i = 0; i < ObjectOperater.PropertiesCount; i++)
            {
                Writer.Write('|');

                Writer.Write(ObjectOperater.GetPropertyName(i));
            }

            for (int i = 0; i < ObjectOperater.PropertiesCount; i++)
            {
                Writer.Write('|');

                Encode(ObjectOperater.GetPropertyValue(Object, i), Writer);
            }

            Writer.Write('}');
        }

        public string Encode(object Value)
        {
            using (var Writer = new StringWriter())
            {
                Encode(Value, Writer);

                return Writer.ToString();
            }
        }

        public string Encode(IList Value)
        {
            using (var Writer = new StringWriter())
            {
                Encode(Value, Writer);

                return Writer.ToString();
            }
        }

        public string Encode(IDictionary Value)
        {
            using (var Writer = new StringWriter())
            {
                Encode(Value, Writer);

                return Writer.ToString();
            }
        }

        public string Encode(IDataReader Value)
        {
            using (var Writer = new StringWriter())
            {
                Encode(Value, Writer);

                return Writer.ToString();
            }
        }

        /// <summary>
        /// 获得一个 .Net 对象的 Eson 文本表现形式
        /// </summary>
        /// <param name="Value">.Net 对象</param>
        /// <returns>返回 Eson 文本</returns>
        public string GetString(object Value)
        {
            if (Value == null)
            {
                return "null";
            }

            if (Value is IConvertible)
            {
                switch (((IConvertible)Value).GetTypeCode())
                {
                    case TypeCode.DBNull:
                        return "null";
                    case TypeCode.Boolean:
                        return (bool)Value ? "true" : "false";
                    case TypeCode.Char:
                        return new string((char)Value, 1);
                    case TypeCode.SByte:
                        return ((sbyte)Value).ToString();
                    case TypeCode.Byte:
                        return ((byte)Value).ToString();
                    case TypeCode.Int16:
                        return ((short)Value).ToString();
                    case TypeCode.UInt16:
                        return ((ushort)Value).ToString();
                    case TypeCode.Int32:
                        return ((int)Value).ToString();
                    case TypeCode.UInt32:
                        return ((uint)Value).ToString();
                    case TypeCode.Int64:
                        return ((long)Value).ToString();
                    case TypeCode.UInt64:
                        return ((ulong)Value).ToString();
                    case TypeCode.Single:
                        return ((float)Value).ToString();
                    case TypeCode.Double:
                        return ((double)Value).ToString();
                    case TypeCode.Decimal:
                        return ((decimal)Value).ToString();
                    case TypeCode.DateTime:
                        return ((DateTime)Value).ToString();
                    case TypeCode.String:
                        goto String;
                }
            }

            if (Value is Guid)
            {
                return ((Guid)Value).ToString("N");
            }

            return null;

            String:

            string StringValue = (string)Value;

            if (StringValue.Length == 0)
            {
                return "";
            }

            switch (StringValue)
            {
                case "null":
                    return "Snull";
                case "true":
                    return "Strue";
                case "false":
                    return "Sfalse";
            }

            bool IsNegative;
            bool IsFloat;
            int Digit;
            int Float;

            if (NumberHelper.IsNumber(StringValue, 10, out IsNegative, out IsFloat, out Digit, out Float))
            {
                return "S" + StringValue;
            }

            StringValue = Escape(StringValue);

            switch (StringValue[0])
            {
                case 'S':
                case 'D':
                    return "S" + StringValue;
            }

            return StringValue;
        }
        /// <summary>
        /// 将 Eson 文本解析到数据写入器
        /// </summary>
        /// <param name="Text">Eson 文本</param>
        /// <param name="Writer">数据写入器</param>
        public unsafe void Decode(string Text, IDataWriter Writer)
        {
            int Begin = 0;

            fixed (char* LpText = Text)
            {
                Decode(LpText, ref Begin, Text.Length, Writer);
            }
        }

        public T Decode<T>(string Text)
        {
            return (T)Decode(Text, typeof(T));
        }

        public object Decode(string Text, Type Type)
        {
            var Writer = BaseReadWriter.Create(Type);

            Decode(Text, Writer);

            return Writer.Content;
        }

        public void Decode(string Text, object Content)
        {
            Decode(Text, BaseReadWriter.Create(Content));
        }

        public void Decode(string Text, IList Array)
        {
            Decode(Text, new ListReadWriter(Array));
        }

        public void Decode(string Text, IDictionary Object)
        {
            Decode(Text, new DictionaryReadWriter(Object));
        }

        private unsafe void Decode(char* LpText, ref int Begin, int End, IDataWriter Writer)
        {
            if (End - Begin < 5)
            {
                throw new FormatException("Text");
            }

            if (LpText[Begin] == '{')
            {
                ++Begin;

                char TypeChar = LpText[Begin];

                Begin += 2;

                switch (TypeChar)
                {
                    case 'T':
                        Writer.ObjectType = RWTypes.Table;

                        DecodeObject(LpText, ref Begin, End, Writer);
                        break;
                    case 'O':
                        Writer.ObjectType = RWTypes.Object;

                        DecodeObject(LpText, ref Begin, End, Writer);
                        break;
                    case 'A':
                        Writer.ObjectType = RWTypes.Array;

                        Writer.Next();

                        DecodeArray(LpText, ref Begin, End, Writer);

                        break;
                    default:
                        throw new Exception("Unsupported type!");
                }
            }
        }

        private unsafe void DecodeArray(char* LpText, ref int Begin, int End, IDataWriter Writer)
        {
            // {A|0}
            // {A|1|Text}

            // 长度为 0，不解析。
            if (LpText[Begin] == '0')
            {
                Begin += 2;

                return;
            }

            int Index;
            int ArrayLength;
            int ArrayIndex = 0;

            Index = StringHelper.IndexOf(LpText, '|', Begin, End);

            ArrayLength = int.Parse(StringHelper.Substring(LpText, Begin, Index));

            Begin = Index + 1;

            DecodeLoop:

            if (LpText[Begin] == '{')
            {
                ++Begin;

                CreateChildrenWriterInfo CCWInfo = new CreateChildrenWriterInfo();

                CCWInfo.Index = ArrayIndex;

                Writer[ArrayIndex] = DecodeUnknow(LpText, ref Begin, End, Writer, CCWInfo);

                ++Begin;
            }
            else
            {
                if (ArrayIndex + 1 == ArrayLength)
                {
                    Index = StringHelper.IndexOf(LpText, '}', Begin, End);
                }
                else
                {
                    Index = StringHelper.IndexOf(LpText, '|', Begin, End);
                }

                Writer[ArrayIndex] = ParseValue(UnEscape(LpText, Begin, Index));

                Begin = Index + 1;
            }

            ++ArrayIndex;

            if (ArrayIndex < ArrayLength)
            {
                goto DecodeLoop;
            }
        }

        private unsafe void DecodeObject(char* LpText, ref int Begin, int End, IDataWriter Writer)
        {
            // {O|0}
            // {O|1|Text}
            // {T|1|Text|Text2}

            // 长度为 0，不解析。
            if (LpText[Begin] == '0')
            {
                Begin += 2;

                return;
            }

            int Index;
            int FieldsCount = 0;
            int FieldIndex = 0;

            Index = StringHelper.IndexOf(LpText, '|', Begin, End);

            string[] Fields = new string[int.Parse(StringHelper.Substring(LpText, Begin, Index))];

            Begin = Index + 1;

            DecodeLoop:

            if (LpText[Begin] == '{')
            {
                ++Begin;

                CreateChildrenWriterInfo CCWInfo = new CreateChildrenWriterInfo();

                CCWInfo.Name = Fields[FieldIndex];

                if (FieldIndex == 0)
                {
                    Writer.Next();
                }

                Writer[Fields[FieldIndex]] = DecodeUnknow(LpText, ref Begin, End, Writer, CCWInfo);
            }
            else
            {
                Index = StringHelper.IndexOf(LpText, '|', '}', Begin, End);

                string StringValue = UnEscape(LpText, Begin, Index);

                Begin = Index;

                if (FieldsCount < Fields.Length)
                {
                    Fields[FieldsCount] = StringValue;

                    ++FieldsCount;

                    goto Return;
                }
                else
                {
                    if (FieldIndex == 0)
                    {
                        Writer.Next();
                    }

                    Writer[Fields[FieldIndex]] = ParseValue(StringValue);
                }
            }

            ++FieldIndex;

            if (FieldIndex == FieldsCount)
            {
                FieldIndex = 0;
            }

            Return:
            if (LpText[Begin++] != '}')
            {
                goto DecodeLoop;
            }
        }

        private unsafe IDataWriter DecodeUnknow(char* LpText, ref int Begin, int End, IDataWriter BaseWriter, CreateChildrenWriterInfo CCWInfo)
        {
            IDataWriter ResultWriter;

            char TypeChar = LpText[Begin];

            Begin += 2;

            switch (TypeChar)
            {
                case 'T':
                    CCWInfo.ObjectType = RWTypes.Table;

                    ResultWriter = BaseWriter.CreateChildrenWriter(CCWInfo);

                    ResultWriter.ObjectType = RWTypes.Table;

                    DecodeObject(LpText, ref Begin, End, ResultWriter);
                    break;
                case 'O':
                    CCWInfo.ObjectType = RWTypes.Object;

                    ResultWriter = BaseWriter.CreateChildrenWriter(CCWInfo);

                    ResultWriter.ObjectType = RWTypes.Object;

                    DecodeObject(LpText, ref Begin, End, ResultWriter);
                    break;
                case 'A':
                    CCWInfo.ObjectType = RWTypes.Array;

                    ResultWriter = BaseWriter.CreateChildrenWriter(CCWInfo);

                    ResultWriter.ObjectType = RWTypes.Array;

                    DecodeArray(LpText, ref Begin, End, ResultWriter);
                    break;
                default:
                    throw new Exception("Unsupported type!");
            }

            return ResultWriter;
        }
    }
}